#include "UdxDataset.h"
#include "UdxDataApi.h"
#include <vector>
#include <algorithm>

namespace NGIS
{
	namespace Data
	{
		bool CUdxDataset::LoadFromXmlFile(const char* fileName)
		{
			tinyxml2::XMLDocument doc;
			doc.LoadFile( fileName );
			tinyxml2::XMLElement* rootEle = doc.RootElement();
			
			for (tinyxml2::XMLElement* ele=rootEle->FirstChildElement(); ele; ele=ele->NextSiblingElement())
			{
				ParseXDO((CUdxNode*)this, ele);
			}

			return true;
		}

		bool CUdxDataset::FormatToXmlFile(const char* fileName)
		{
			tinyxml2::XMLDocument* doc = new tinyxml2::XMLDocument();
			tinyxml2::XMLElement* element = doc->NewElement("dataset");
			doc->LinkEndChild(element);

			int count = this->getChildNodeCount();
			for (int iNode=0; iNode<count; iNode++)
			{
				IUdxNode* tempNode = this->getChildNode(iNode);
				FormatXDO(tempNode, element);
			}

			doc->SaveFile(fileName);
			delete doc;

			return true;
		}

		bool CUdxDataset::LoadFromXmlStream(const char* xmlStr)
		{
			tinyxml2::XMLDocument doc;
			doc.Parse( xmlStr );
			tinyxml2::XMLElement* rootEle = doc.RootElement();

			for (tinyxml2::XMLElement* ele=rootEle->FirstChildElement(); ele; ele=ele->NextSiblingElement())
			{
				ParseXDO((CUdxNode*)this, ele);
			}

			return true;
		}

		bool CUdxDataset::FormatToXmlStream(std::string& xmlStr)
		{
			tinyxml2::XMLDocument doc;
			tinyxml2::XMLElement* element = doc.NewElement("dataset");
			doc.LinkEndChild(element);

			int count = this->getChildNodeCount();
			for (int iNode=0; iNode<count; iNode++)
			{
				IUdxNode* tempNode = this->getChildNode(iNode);
				FormatXDO(tempNode, element);
			}

			tinyxml2::XMLPrinter printer;
			doc.Print(&printer);
			
			xmlStr = printer.CStr();
			return true;
		}

		void split(const std::string& s, const std::string& delim, std::vector<std::string>* ret) 
		{  
			size_t last = 0;   
			size_t index=s.find_first_of(delim,last);  
			while (index!=std::string::npos)  
			{   
				ret->push_back(s.substr(last,index-last));   
				last=index+1;    
				index=s.find_first_of(delim,last);  
			}   
			if (index-last>0)  
			{     
				ret->push_back(s.substr(last,index-last));
			}
		}

		void CUdxDataset::ParseXDO(IUdxNode* containerNode, tinyxml2::XMLElement* element)
		{
			std::string typeStr = element->Attribute("kernelType");
			const char* name = element->Attribute("name");
			EKernelType kernelType = String2KernelType(typeStr.c_str());

			if (kernelType == EKernelType::EKT_INT)
			{
				int val = element->IntAttribute("value");
				IUdxNode* node = containerNode->addChildNode(name, kernelType);
				CUdxKernelIntValue* realKernel = (CUdxKernelIntValue*)node->getKernel();
				realKernel->setTypedValue(val);
			}
			else if (kernelType == EKernelType::EKT_REAL)
			{
				double val = element->DoubleAttribute("value");
				IUdxNode* node = containerNode->addChildNode(name, kernelType);
				CUdxKernelRealValue* realKernel = (CUdxKernelRealValue*)node->getKernel();
				realKernel->setTypedValue(val);
			}
			else if (kernelType == EKernelType::EKT_STRING)
			{
				const char* val = element->Attribute("value");
				IUdxNode* node = containerNode->addChildNode(name, kernelType);
				CUdxKernelStringValue* realKernel = (CUdxKernelStringValue*)node->getKernel();
				realKernel->setTypedValue(val);
			}
			else if (kernelType == EKernelType::EKT_VECTOR2)
			{
				const char* valStr = element->Attribute("value");
				IUdxNode* node = containerNode->addChildNode(name, kernelType);
				CUdxKernelVector2dValue* realKernel = (CUdxKernelVector2dValue*)node->getKernel();
				double x, y;
				sscanf(valStr, "%lf,%lf", &x, &y);
				realKernel->setTypedValue(x, y);
			}
			else if (kernelType == EKernelType::EKT_VECTOR3)
			{
				const char* valStr = element->Attribute("value");
				IUdxNode* node = containerNode->addChildNode(name, kernelType);
				CUdxKernelVector3dValue* realKernel = (CUdxKernelVector3dValue*)node->getKernel();
				double x, y, z;
				sscanf(valStr, "%lf,%lf,%lf", &x, &y, &z);
				realKernel->setTypedValue(x, y, z);
			}
			else if (kernelType == EKernelType::EKT_VECTOR4)
			{
				const char* valStr = element->Attribute("value");
				IUdxNode* node = containerNode->addChildNode(name, kernelType);
				CUdxKernelVector4dValue* realKernel = (CUdxKernelVector4dValue*)node->getKernel();
				double x, y, z, m;
				sscanf(valStr, "%lf,%lf,%lf,%lf", &x, &y, &z, &m);
				realKernel->setTypedValue(x, y, z, m);
			}
			else if (kernelType == EKernelType::EKT_INT_LIST)
			{
				std::string valStr = element->Attribute("value");
				std::vector<std::string> ret;
				split(valStr, ",", &ret);
				int count = ret.size();
				{
					IUdxNode* node = containerNode->addChildNode(name, kernelType);
					CUdxKernelIntArray* realKernel = (CUdxKernelIntArray*)node->getKernel();
					for (int iVal=0; iVal<count; iVal++)
					{
						realKernel->addTypedValue(atoi(ret[iVal].c_str()));
					}
				}
			}
			else if (kernelType == EKernelType::EKT_REAL_LIST)
			{
				std::string valStr = element->Attribute("value");
				std::vector<std::string> ret;
				split(valStr, ",", &ret);
				int count = ret.size();
				{
					IUdxNode* node = containerNode->addChildNode(name, kernelType);
					CUdxKernelRealArray* realKernel = (CUdxKernelRealArray*)node->getKernel();
					for (int iVal=0; iVal<count; iVal++)
					{
						realKernel->addTypedValue(atof(ret[iVal].c_str()));
					}
				}
			}
			else if (kernelType == EKernelType::EKT_STRING_LIST)
			{
				std::string valStr = element->Attribute("value");
				std::vector<std::string> ret;
				split(valStr, ";", &ret);
				int count = ret.size();
				{
					IUdxNode* node = containerNode->addChildNode(name, kernelType);
					CUdxKernelStringArray* realKernel = (CUdxKernelStringArray*)node->getKernel();
					for (int iVal=0; iVal<count; iVal++)
					{
						realKernel->addTypedValue(ret[iVal].c_str());
					}
				}
			}
			else if (kernelType == EKernelType::EKT_VECTOR2_LIST)
			{
				std::string valStr = element->Attribute("value");
				std::vector<std::string> ret;
				split(valStr, ";", &ret);
				int count = ret.size();
				{
					IUdxNode* node = containerNode->addChildNode(name, kernelType);
					CUdxKernelVector2dArray* realKernel = (CUdxKernelVector2dArray*)node->getKernel();
					for (int iVal=0; iVal<count; iVal++)
					{
						double x, y;
						sscanf(ret[iVal].c_str(), "%lf,%lf", &x, &y);
						realKernel->addTypedValue(x, y);
					}
				}
			}
			else if (kernelType == EKernelType::EKT_VECTOR3_LIST)
			{
				const char* valStr = element->Attribute("value");
				std::vector<std::string> ret;
				split(valStr, ";", &ret);
				int count = ret.size();
				{
					IUdxNode* node = containerNode->addChildNode(name, kernelType);
					CUdxKernelVector3dArray* realKernel = (CUdxKernelVector3dArray*)node->getKernel();
					for (int iVal=0; iVal<count; iVal++)
					{
						double x, y, z;
						sscanf(ret[iVal].c_str(), "%lf,%lf,%lf", &x, &y, &z);
						realKernel->addTypedValue(x, y, z);
					}
				}
			}
			else if (kernelType == EKernelType::EKT_VECTOR4_LIST)
			{
				std::string valStr = element->Attribute("value");
				std::vector<std::string> ret;
				split(valStr, ";", &ret);
				int count = ret.size();
				{
					IUdxNode* node = containerNode->addChildNode(name, kernelType);
					CUdxKernelVector4dArray* realKernel = (CUdxKernelVector4dArray*)node->getKernel();
					for (int iVal=0; iVal<count; iVal++)
					{
						double x, y, z, m;
						sscanf(ret[iVal].c_str(), "%lf,%lf,%lf,%lf", &x, &y, &z, &m);
						realKernel->addTypedValue(x, y, z, m);
					}
				}
			}
			else if (kernelType == EKernelType::EKT_NODE ||
					  kernelType == EKernelType::EKT_LIST ||
					  kernelType == EKernelType::EKT_MAP ||
					  kernelType == EKernelType::EKT_TABLE )
			{
				IUdxNode* node = containerNode->addChildNode(name, kernelType);
				for (tinyxml2::XMLElement* childEle = element->FirstChildElement(); childEle; childEle = childEle->NextSiblingElement())
				{
					ParseXDO(node, childEle);
				}
			}
			
		}

		void CUdxDataset::FormatXDO(IUdxNode* pNode, tinyxml2::XMLElement* element)
		{
			EKernelType kernelType = pNode->getKernel()->getType();
			const char* name = pNode->getName();
			tinyxml2::XMLDocument* doc = element->GetDocument();
			tinyxml2::XMLElement* childEle = doc->NewElement("XDO");
			element->LinkEndChild(childEle);
			childEle->SetAttribute("name", name);
			if (kernelType == EKernelType::EKT_INT)
			{
				{
					IUdxKernelIntValue* realKernel = (IUdxKernelIntValue*)pNode->getKernel();
					int val = realKernel->getTypedValue();
					childEle->SetAttribute("kernelType", KernelType2String(kernelType).c_str());
					childEle->SetAttribute("value", val);
				}
			}
			else if (kernelType == EKernelType::EKT_REAL)
			{
				{
					IUdxKernelRealValue* realKernel = (IUdxKernelRealValue*)pNode->getKernel();
					double val = realKernel->getTypedValue();
					childEle->SetAttribute("kernelType", KernelType2String(kernelType).c_str());
					childEle->SetAttribute("value", val);
				}
			}
			else if (kernelType == EKernelType::EKT_STRING)
			{
				{
					IUdxKernelStringValue* realKernel = (IUdxKernelStringValue*)pNode->getKernel();
					std::string val = realKernel->getTypedValue();
					childEle->SetAttribute("kernelType", KernelType2String(kernelType).c_str());
					childEle->SetAttribute("value", val.c_str());
				}
			}
			else if (kernelType == EKernelType::EKT_VECTOR2)
			{
				{
					IUdxKernelVector2dValue* realKernel = (IUdxKernelVector2dValue*)pNode->getKernel();
					Vector2d v = realKernel->getTypedValue();
					char vector2dStr[100];
					sprintf(vector2dStr, "%f,%f", v.x, v.y);
					childEle->SetAttribute("kernelType", KernelType2String(kernelType).c_str());
					childEle->SetAttribute("value", vector2dStr);
				}
			}
			else if (kernelType == EKernelType::EKT_VECTOR3)
			{
				{
					IUdxKernelVector3dValue* realKernel = (IUdxKernelVector3dValue*)pNode->getKernel();
					Vector3d v =realKernel->getTypedValue();
					char vector3dStr[100];
					sprintf(vector3dStr, "%f,%f,%f", v.x, v.y, v.z);
					childEle->SetAttribute("kernelType", KernelType2String(kernelType).c_str());
					childEle->SetAttribute("value", vector3dStr);
				}
			}
			else if (kernelType == EKernelType::EKT_VECTOR4)
			{
				{
					IUdxKernelVector4dValue* realKernel = (IUdxKernelVector4dValue*)pNode->getKernel();
					Vector4d v =realKernel->getTypedValue();
					char vector4dStr[100];
					sprintf(vector4dStr, "%f,%f,%f,%f", v.x, v.y, v.z, v.m);
					childEle->SetAttribute("kernelType", KernelType2String(kernelType).c_str());
					childEle->SetAttribute("value", vector4dStr);
				}
			}
			else if (kernelType == (EKernelType)(EKernelType::EKT_INT|EKernelType::EKT_LIST))
			{
				{
					IUdxKernelIntArray* realKernel = (IUdxKernelIntArray*)pNode->getKernel();
					int count = realKernel->getCount();
					std::string valStr = "";
					for (int iVal=0; iVal<count; iVal++)
					{
						int val = 0;
						if (realKernel->getTypedValueByIndex(iVal, val)==false)
							continue;
						char tempValChar[100];
						if (iVal!=count-1)
							sprintf(tempValChar, "%d, ", val);
						else
							sprintf(tempValChar, "%d", val);
						valStr += tempValChar;
					}
					childEle->SetAttribute("kernelType", KernelType2String(kernelType).c_str());
					childEle->SetAttribute("value", valStr.c_str());
				}
			}
			else if (kernelType == (EKernelType)(EKernelType::EKT_REAL|EKernelType::EKT_LIST))
			{
				{
					IUdxKernelRealArray* realKernel = (IUdxKernelRealArray*)pNode->getKernel();
					int count = realKernel->getCount();
					std::string valStr = "";
					for (int iVal=0; iVal<count; iVal++)
					{
						double val = 0;
						if (realKernel->getTypedValueByIndex(iVal, val)==false)
							continue;
						char tempValChar[100];
						if (iVal!=count-1)
							sprintf(tempValChar, "%lf, ", val);
						else
							sprintf(tempValChar, "%lf", val);
						valStr += tempValChar;
					}
					childEle->SetAttribute("kernelType", KernelType2String(kernelType).c_str());
					childEle->SetAttribute("value", valStr.c_str());
				}
			}
			else if (kernelType == (EKernelType)(EKernelType::EKT_STRING|EKernelType::EKT_LIST))
			{
				{
					IUdxKernelStringArray* realKernel = (IUdxKernelStringArray*)pNode->getKernel();
					int count = realKernel->getCount();
					std::string valStr = "";
					for (int iVal=0; iVal<count; iVal++)
					{
						std::string val = "";
						if (realKernel->getTypedValueByIndex(iVal, val)==false)
							continue;
						char tempValChar[100];
						if (iVal!=count-1)
						{
							valStr += val;
							valStr += "; ";
						}
						else
							valStr += val;
					}
					childEle->SetAttribute("kernelType", KernelType2String(kernelType).c_str());
					childEle->SetAttribute("value", valStr.c_str());
				}
			}
			else if (kernelType == (EKernelType)(EKernelType::EKT_VECTOR2|EKernelType::EKT_LIST))
			{
				{
					IUdxKernelVector2dArray* realKernel = (IUdxKernelVector2dArray*)pNode->getKernel();
					int count = realKernel->getCount();
					std::string valStr = "";
					for (int iVal=0; iVal<count; iVal++)
					{
						Vector2d val;
						if (realKernel->getTypedValueByIndex(iVal, val)==false)
							continue;
						char tempValChar[100];
						if (iVal!=count-1)
						{
							sprintf(tempValChar, "%lf,%lf; ", val.x, val.y);
						}
						else
							sprintf(tempValChar, "%lf,%lf", val.x, val.y);
						valStr += tempValChar;
					}
					childEle->SetAttribute("kernelType", KernelType2String(kernelType).c_str());
					childEle->SetAttribute("value", valStr.c_str());
				}
			}
			else if (kernelType == (EKernelType)(EKernelType::EKT_VECTOR3|EKernelType::EKT_LIST))
			{
				{
					IUdxKernelVector3dArray* realKernel = (IUdxKernelVector3dArray*)pNode->getKernel();
					int count = realKernel->getCount();
					std::string valStr = "";
					for (int iVal=0; iVal<count; iVal++)
					{
						Vector3d val;
						if (realKernel->getTypedValueByIndex(iVal, val)==false)
							continue;
						char tempValChar[100];
						if (iVal!=count-1)
						{
							sprintf(tempValChar, "%lf,%lf,%lf; ", val.x, val.y, val.z);
						}
						else
							sprintf(tempValChar, "%lf,%lf,%lf", val.x, val.y, val.z);
						valStr += tempValChar;
					}
					childEle->SetAttribute("kernelType", KernelType2String(kernelType).c_str());
					childEle->SetAttribute("value", valStr.c_str());
				}
			}
			else if (kernelType == (EKernelType)(EKernelType::EKT_VECTOR4|EKernelType::EKT_LIST))
			{
				{
					IUdxKernelVector4dArray* realKernel = (IUdxKernelVector4dArray*)pNode->getKernel();
					int count = realKernel->getCount();
					std::string valStr = "";
					for (int iVal=0; iVal<count; iVal++)
					{
						Vector4d val;
						if (realKernel->getTypedValueByIndex(iVal, val)==false)
							continue;
						char tempValChar[100];
						if (iVal!=count-1)
						{
							sprintf(tempValChar, "%lf,%lf,%lf,%lf; ", val.x, val.y, val.z, val.m);
						}
						else
							sprintf(tempValChar, "%lf,%lf,%lf,%lf", val.x, val.y, val.z, val.m);
						valStr += tempValChar;
					}
					childEle->SetAttribute("kernelType", KernelType2String(kernelType).c_str());
					childEle->SetAttribute("value", valStr.c_str());
				}
			}
			else if (kernelType == EKernelType::EKT_NODE ||
					   kernelType == EKernelType::EKT_LIST || 
					   kernelType == EKernelType::EKT_MAP ||
					   kernelType == EKernelType::EKT_TABLE)
			{
				childEle->SetAttribute("kernelType", KernelType2String(kernelType).c_str());
				int count = pNode->getChildNodeCount();
				for (int iNode=0; iNode<count; iNode++)
				{
					IUdxNode* tempNode = pNode->getChildNode(iNode);
					FormatXDO(tempNode, childEle);
				}
			}
		}

		bool CUdxDataset::LoadFromJsonFile(const char* fileName)
		{
			return false;
		}

		bool CUdxDataset::FormatToJsonFile(const char* fileName)
		{
			return false;
		}

		bool CUdxDataset::LoadFromJsonStream(const char* jsonStr)
		{
			return false;
		}

		bool CUdxDataset::FormatToJsonStream(std::string& jsonStr)
		{
			return false;
		}

		bool CUdxDataset::constructDataFromSchema( Schema::IUdxNodeSchema* pSNode )
		{
			for (int i = 0; i < pSNode->getChildNodeCount(); i++)
			{
				Schema::IUdxNodeSchema* tempSNode = pSNode->getChildNode(i);
				std::string name = tempSNode->getName();
				Schema::ESchemaNodeType pSType = tempSNode->getDescription()->getKernelType();
				EKernelType pDType = getKernelTypeBySchema(pSType);
				IUdxNode* tempDNode = this->addChildNode(name.c_str(), pDType);
				IterateChildNodes(tempSNode, tempDNode);
			}
			return true;
		}

		//////////////////////////////////////////////////////////////////////////
		//
		//
		//
		//////////////////////////////////////////////////////////////////////////
		extern "C" NGISDATA_API const char* NGISDATA_CALLCONV getVersion()
		{
			return "1.0.0.0";
		}

		extern "C" NGISDATA_API IUdxDataset* NGISDATA_CALLCONV createUdxDataset(const char* pName)
		{
			return new CUdxDataset(pName);
		}
	}
}